package com.yj.lottery_system.service.mq;

import com.yj.lottery_system.common.exception.ServiceException;
import com.yj.lottery_system.common.utils.EmailCodeUtil;
import com.yj.lottery_system.common.utils.JacksonUtil;
import com.yj.lottery_system.controller.param.DrawPrizeParam;
import com.yj.lottery_system.dao.dataObject.ActivityPrizeDO;
import com.yj.lottery_system.dao.dataObject.WinningRecodesDO;
import com.yj.lottery_system.dao.mapper.ActivityPrizeMapper;
import com.yj.lottery_system.dao.mapper.WinningRecordMapper;
import com.yj.lottery_system.service.IDrawPrizeService;
import com.yj.lottery_system.service.activityStatus.IActivityStatusManager;
import com.yj.lottery_system.service.dto.ConvertActivityStatusDTO;
import com.yj.lottery_system.service.enums.ActivityPrizeStatusEnum;
import com.yj.lottery_system.service.enums.ActivityStatusEnum;
import com.yj.lottery_system.service.enums.ActivityUserStatusEnum;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.yj.lottery_system.common.config.DirectRabbitConfig.QUEUE_NAME;

@RabbitListener(queues = QUEUE_NAME)
@Component
public class MqReceiver {
    private static final Logger log = LoggerFactory.getLogger(MqReceiver.class);
   @Resource
    private IDrawPrizeService drawPrizeService;
   @Resource
   private IActivityStatusManager activityStatusManager;
   @Autowired
   @Qualifier("asyncServiceExecutor")
   private ThreadPoolTaskExecutor threadPoolTaskExecutor;
   @Autowired
    private EmailCodeUtil emailCodeUtil;
   @Resource
   private ActivityPrizeMapper activityPrizeMapper;
   @Resource
   private WinningRecordMapper winningRecordMapper;
    @RabbitHandler
    public void process(Map<String,String> message)  throws Exception{
        //成功接收队列消息
        log.info("mq成功接收到消息 message：{}",
                JacksonUtil.writeValueAsString(message));

        String paramString = message.get("messageData");
        DrawPrizeParam param = JacksonUtil.readValue(paramString,DrawPrizeParam.class);

        //处理抽奖
        try {
            //校验抽奖请求
            if(!drawPrizeService.checkDrawPrizeParam(param)) {
                return;
            }
            //活动，奖品，人员 状态扭转
            statusConvert(param);
            //保存中奖者名单
            List<WinningRecodesDO> winningRecodesDOList = drawPrizeService.saveWinnerRecodes(param);
            //通知中奖者
            syncExecute(winningRecodesDOList);
        }catch (ServiceException e) {
            log.error("处理 MQ 消息异常 {}， {} ",e.getCode(),e.getMsg(),e);
            //发生异常，需要回滚 保持事务一致性，
            rollBack(param);
            // 抛出异常
            throw e;
        } catch (Exception e) {
            log.error("处理 MQ 消息异常  ",e);
            //发生异常，需要回滚 保持事务一致性，
            rollBack(param);
            // 抛出异常
           throw e;
        }

    }

    /**
     * 处理抽奖异常回滚行为
     * @param param
     */
    private void rollBack(DrawPrizeParam param) {
        //回滚状态：活动表 奖品 人员
        //状态不需要回滚直接return
        if(!statusNeedRollBack(param)) {
            return;
        }
        rollBackStatus(param);
        //回滚中奖者名单
        //是否需要回滚，不需要直接返回
        if(!winnerNeedRollBack(param)) {
            return;
        }
        rollBackWinner(param);
    }

    /**
     * 回滚中奖者名单
     * @param param
     */
    private void rollBackWinner(DrawPrizeParam param) {
        drawPrizeService.deleteRecords(param.getActivityId(), param.getPrizeId());
    }
    /**
     * 判断中奖者名单是否需要回滚，需要-true
     * @param param
     */
    private boolean winnerNeedRollBack(DrawPrizeParam param) {
        //判断活动中的奖品是否存在中奖者
        int count = winningRecordMapper.count(param.getActivityId(), param.getPrizeId());

        return count > 0;
    }
    /**
     * 回滚状态
     * @param param
     */
    private void rollBackStatus(DrawPrizeParam param) {
        ConvertActivityStatusDTO convertActivityStatusDTO = new ConvertActivityStatusDTO();
        convertActivityStatusDTO.setActivityId(param.getActivityId());
        convertActivityStatusDTO.setTargetActivityStatus(ActivityStatusEnum.RUNNING);
        convertActivityStatusDTO.setPrizeId(param.getPrizeId());
        convertActivityStatusDTO.setTargetActivityPrizeStatus(ActivityPrizeStatusEnum.INIT);
        convertActivityStatusDTO.setUserIds(
                param.getWinnerList().stream()
                        .map(DrawPrizeParam.Winner::getUserId)
                        .collect(Collectors.toList())
        );
        convertActivityStatusDTO.setTargetActivityUserStatus(ActivityUserStatusEnum.INIT);

        activityStatusManager.rollBackHandleEvent(convertActivityStatusDTO);
    }
    /**
     * 判断状态是否需要回滚，需要-true
     * @param param
     */
    private boolean statusNeedRollBack(DrawPrizeParam param) {
        //扭转状态时保证了事务一致性，只需要判断一个状态（不包含活动）
        ActivityPrizeDO activityPrizeDO =
                activityPrizeMapper.selectByAPId(param.getActivityId(), param.getPrizeId());
        //已经扭转需要回滚
        return activityPrizeDO.getStatus()
                        .equalsIgnoreCase(ActivityPrizeStatusEnum.COMPLETED.name());

    }

    //通知
    private void syncExecute(List<WinningRecodesDO> winningRecodesDOList) {
        //邮件通知
        threadPoolTaskExecutor.execute(()->sendMail(winningRecodesDOList));
    }

    /**
     * 发邮件
     * @param winningRecodesDOList
     */
    private void sendMail(List<WinningRecodesDO> winningRecodesDOList) {
        if(CollectionUtils.isEmpty(winningRecodesDOList)) {
            log.info("中奖列表为空，不用发邮件");
            return;
        }
        for(WinningRecodesDO winningRecodesDO : winningRecodesDOList) {
            String context = "您好， "+winningRecodesDO.getWinnerName()+"，恭喜您在"+
                    winningRecodesDO.getActivityName()+"抽奖活动中获得"+
                    winningRecodesDO.getPrizeTier()+"："+
                    winningRecodesDO.getPrizeName()+"！获奖时间是："+
                    winningRecodesDO.getWinningTime()+"，请尽快领取您的奖品，祝您生活愉快。";
            emailCodeUtil.sendSampleMail(winningRecodesDO.getWinnerEmail(),
                    "中奖通知", context);
        }
    }


    /**
     * 状态扭转
     * @param param
     */
    private void statusConvert(DrawPrizeParam param) {
        ConvertActivityStatusDTO convertActivityStatusDTO = new ConvertActivityStatusDTO();
        convertActivityStatusDTO.setActivityId(param.getActivityId());
        convertActivityStatusDTO.setTargetActivityStatus(ActivityStatusEnum.COMPLETED);
        convertActivityStatusDTO.setPrizeId(param.getPrizeId());
        convertActivityStatusDTO.setTargetActivityPrizeStatus(ActivityPrizeStatusEnum.COMPLETED);
        convertActivityStatusDTO.setUserIds(param.getWinnerList().stream()
                        .map(DrawPrizeParam.Winner::getUserId)
                        .collect(Collectors.toList())
        );
        convertActivityStatusDTO.setTargetActivityUserStatus(ActivityUserStatusEnum.COMPLETED);

        //调用状态扭转方法
        activityStatusManager.handleEvent(convertActivityStatusDTO);

    }
}
